// 索引类型的重新构造

type obj = {
  name: string;
  age: number;
  gender: boolean;
};

{
  type obj2 = {
    readonly name: string;
    age?: number;
    gender: boolean;
  };

  // 映射类型语法

  type Mapping<Obj extends object> = {
    [key in keyof Obj]: Obj[key];
  };
}

{
  // UppercaseKey

  // 除了可以对 Value 做修改，也可以对 Key 做修改，使用 as，这叫做重映射：
  // 比如把索引类型的 Key 变为大写。

  type UppercaseKey<Obj extends object> = {
    [key in keyof Obj as Uppercase<key & string>]: Obj[key];
  };

  type res = UppercaseKey<obj>;

  /*
    type res = {
      NAME: string;
      AGE: number;
      GENDER: boolean;
    }
  */

  /**
   * 类型参数 Obj 是待处理的索引类型，通过 extends 约束为 object。
   *
   * 新的索引类型的索引为 Obj 中的索引，也就是 Key in keyof Obj，但要做一些变换，也就是 as 之后的。
   *
   * 通过 Uppercase 把索引 Key 转为大写，因为索引可能为 string、number、symbol 类型，而这里只能接受 string 类型，所以要 & string，也就是取索引中 string 的部分。
   *
   * value 保持不变，也就是之前的索引 Key 对应的值的类型 Obj[Key]。
   *
   * 这样构造出的新的索引类型，就把原来索引类型的索引转为了大写：
   */
}

{
  // Record ts内置类型

  type Record<T extends keyof any, K> = {
    [P in T]: K;
  };

  type UppercaseKey<Obj extends Record<string, any>> = {
    [Key in keyof Obj as Uppercase<Key & string>]: Obj[Key];
  };
}

{
  // ToReadonly > Readonly

  type ToReadonly<T> = {
    readonly [Key in keyof T]: T[Key];
  };
}

{
  // ToPartial > Partial

  type ToPartial<T> = {
    [Key in keyof T]?: T[Key];
  };

  type res = ToPartial<obj>;
}

{
  // 去除readonly修饰

  type ToMutable<T> = {
    -readonly [Key in keyof T]: T[Key];
  };

  type res = ToMutable<Readonly<obj>>;
}

{
  // ToRquired > Required 将所有可选修饰符去除 就是必选项

  type ToRquired<T> = {
    [Key in keyof T]-?: T[Key];
  };

  // Partial 将所有参数变为可选项

  type res = Partial<obj>;
  type res2 = ToRquired<res>;
}

{
  // 根据值类型进行过过滤

  type FilterByValueType<Obj extends Record<string, any>, ValueType> = {
    [Key in keyof Obj as ValueType extends Obj[Key] ? Key : never]: Obj[Key];
  };

  type res = FilterByValueType<obj, string | number>;

  // Pick 根据键进行过滤

  type res2 = Pick<obj, 'age' | 'gender'>;
}

// 对索引做修改的 as 叫做重映射

export {};
